package com.laiat.yamba;

import java.util.List;
import java.util.Locale;

import oauth.signpost.OAuth;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.auth.AccessToken;
import android.app.Activity;
import android.app.AlarmManager;
import android.app.Application;
import android.app.PendingIntent;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.preference.PreferenceManager;
import android.speech.tts.TextToSpeech;
import android.text.TextUtils;
import android.util.Log;
import android.widget.Toast;

import com.laiat.yamba.activity.PrefsActivity;
import com.laiat.yamba.activity.PrepareRequestTokenActivity;

public class YambaApplication extends Application implements TextToSpeech.OnInitListener, OnSharedPreferenceChangeListener {
	private static final String TAG = YambaApplication.class.getSimpleName();
	
	public static final String BROADCAST_TWITTER_RESULT = "com.laiat.yamba.intent.TwitterResult";
	public static final String OAUTH_LOGIN_EXCEPTION = "com.laiat.yamba.intent.OAuthLoginException";
	public static final String OAUTH_TEXT = "com.laiat.yamba.intent.OAuthResultText";
	
	public static final int UPDATE_INTERVAL_NEVER = 0;
	public static final String LOCATION_PROVIDER_NONE = "NONE";
	
	private static final String PREF_KEY_APP_HAS_ALREADY_RUN_ONCE = "appHasAlreadyRunOnceFlag";
	
	private Twitter twitter;
	private SharedPreferences prefs;
	private StatusData statusData;
	
	private String consumerKey;
	private String consumerSecret;
	
	private boolean serviceRunning;
	
	private TextToSpeech textToSpeech = null;
	private boolean bTextToSpeechInit = true;

	@Override
	public void onCreate() {
		super.onCreate();
        prefs = PreferenceManager.getDefaultSharedPreferences(this);
        prefs.registerOnSharedPreferenceChangeListener(this);
        
        boolean hasAppAlreadyRun = prefs.getBoolean(PREF_KEY_APP_HAS_ALREADY_RUN_ONCE, false);
        if (!hasAppAlreadyRun) {
        	resetUpdateAlarm();
        	Editor prefsEditor = prefs.edit();
        	prefsEditor.putBoolean(PREF_KEY_APP_HAS_ALREADY_RUN_ONCE, true);
        	prefsEditor.commit();
        }
        
 		
        consumerKey = getString(R.string.consumer_key);
        consumerSecret = getString(R.string.consumer_secret);
        
        statusData = new StatusData(this);

		Log.i(TAG, "onCreated");
	}

	@Override
	public void onTerminate() {
		super.onTerminate();
		Log.i(TAG, "onTerminated");
	}
	
	public synchronized void removeTwitterCredentials() {
		Editor editor = prefs.edit();
		editor.remove(OAuth.OAUTH_TOKEN);
		editor.remove(OAuth.OAUTH_TOKEN_SECRET);
		editor.commit();

		twitter = null;
	}
	
	public synchronized TextToSpeech getTextToSpeech() {
		return getTextToSpeech(true);
	}
	
	public synchronized TextToSpeech getTextToSpeech(boolean bOnlyInitialized) {
		if (!bOnlyInitialized) {
			return textToSpeech;
		}
		return bTextToSpeechInit ? textToSpeech : null;
	}
	
    public void setTextToSpeech(TextToSpeech textToSpeech) {
		this.textToSpeech = textToSpeech;
	}

	@Override
	public void onInit(int status) {
		bTextToSpeechInit = true; 
		textToSpeech.setLanguage(Locale.US);
    	Log.i(TAG, "TextToSpeech engine initialized.");
	}

	public synchronized Twitter getTwitter() {
    	if (twitter == null) {
    		
    		String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
			String tokenSecret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");
    		
			if (!TextUtils.isEmpty(token) && !TextUtils.isEmpty(tokenSecret)) {
				//connect to twitter.com
	    		twitter = new TwitterFactory().getInstance();
	    		twitter.setOAuthConsumer(consumerKey, consumerSecret);
	    		twitter.setOAuthAccessToken(new AccessToken(token, tokenSecret));
				Log.d(TAG, "twitter object was created.");
			}
    	}
    	
    	return twitter;
    }
	
//	public synchronized boolean verifyTwitterCredentials() {
//		if (twitter == null) {
//			return false;
//		}
//		
//		try {
//			twitter.verifyCredentials();
//			return true;
//		} catch (TwitterException e) {
//			
//			Log.w(TAG, "TwitterException thrown trying to verify twitter credentials.", e);
//			twitter = null;
//
//			processTwitterException(e);
//			
//		}
//
//		return false;
//	}
	
	public boolean hasTwitterCredentials() {
		String token = prefs.getString(OAuth.OAUTH_TOKEN, "");
		String tokenSecret = prefs.getString(OAuth.OAUTH_TOKEN_SECRET, "");
		
		return !TextUtils.isEmpty(token) && !TextUtils.isEmpty(tokenSecret);
		
	}

	public void loginToTwitter() {
		//do twitter login
		Log.d(TAG, "sign on to twitter.");
		Intent intent = new Intent(this, PrepareRequestTokenActivity.class);
		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		startActivity(intent);
	}
    
    // Connects to the online service and puts the latest statuses into DB
    // Returns the count of new statuses
    public synchronized int fetchStatusUpdates() {
    	Log.d(TAG, "Fetching status updates");
    	twitter = this.getTwitter();
    	if (twitter == null) {
    		Log.d(TAG, "Twitter connection info not initialized");
    		return 0;
    	}
    	
    	try {
			//Get the timeline from the cloud
    		List<Status> statusUpdates = twitter.getFriendsTimeline();
    		
    		long latestStatusCreatedAtTime = this.getStatusData().getLatestStatusCreatedAtTime();
    		int count = 0;

			//loop over the timeline and insert into db and log it
			ContentValues values = new ContentValues();
			for (Status status : statusUpdates) {
				//Insert into db
				values.clear();
				values.put(StatusData.C_ID, Long.toString(status.getId()));
				long createdAt = status.getCreatedAt().getTime();
				values.put(StatusData.C_CREATED_AT, createdAt);
				values.put(StatusData.C_SOURCE, status.getSource());
				values.put(StatusData.C_TEXT, status.getText());
				values.put(StatusData.C_USER, status.getUser().getName());
				
				 Log.d(TAG, "Got update with id " + status.getId() + ". Saving");
				 this.getStatusData().insertOrIgnore(values);
				 if (latestStatusCreatedAtTime < createdAt) {
					 count++;
				 }
			}
			
			Log.d(TAG, count > 0 ? "Got " + count + " status updates" : "No new status updates");
    		
			return count;
		} catch(TwitterException e) {
			Log.e(TAG, "Failed to connect to twitter service", e);
			
			processTwitterException(e);

			
		} catch(RuntimeException e) {
			Log.e(TAG, "Failed to fetch status updates", e);
		}
		return 0;
    }
    
    public void processTwitterException(TwitterException e) {
    	processTwitterException(e, null);
    }

	public void processTwitterException(TwitterException e, Activity activity) {
		if (e.getStatusCode() == TwitterException.UNAUTHORIZED) {
			removeTwitterCredentials();
			if (activity != null) {
				Toast.makeText(activity, "Twitter Credentials are no longer valid", Toast.LENGTH_LONG);
			} else {
				//send error message
				Intent intent = new Intent(YambaApplication.BROADCAST_TWITTER_RESULT);
		   		intent.putExtra(YambaApplication.OAUTH_TEXT, "Twitter Credentials are no longer valid");
				intent.putExtra(YambaApplication.OAUTH_LOGIN_EXCEPTION, e);
				
				sendBroadcast(intent, Manifest.permission.SEND_TWITTER_RESULT);
				
			}
		}
	}

	public String getConsumerKey() {
		return consumerKey;
	}

	public String getConsumerSecret() {
		return consumerSecret;
	}

	public boolean isServiceRunning() {
		return serviceRunning;
	}

	public void setServiceRunning(boolean serviceRunning) {
		this.serviceRunning = serviceRunning;
	}

	public SharedPreferences getPrefs() {
		return prefs;
	}

	public StatusData getStatusData() {
		return statusData;
	}
	
	public String getProvider() {
		return prefs.getString(PrefsActivity.KEY_LOCATION_PROVIDER, LOCATION_PROVIDER_NONE);
	}
	
	
	
	@Override
	public synchronized void onSharedPreferenceChanged(SharedPreferences sp, String key) {
		if (key.equals(PrefsActivity.KEY_UPDATE_INTERVAL)) {

			resetUpdateAlarm();
			
		}
	}

	public void resetUpdateAlarm() {
		Context context = getApplicationContext();
		cancelUpdateAlarm();

		// Create the pending intent
		PendingIntent pendingIntent = createUpdateAlarmPendingIntent();

		// Check if we should do start update service
		long updateInterval = Integer.parseInt(getPrefs().getString(PrefsActivity.KEY_UPDATE_INTERVAL, 
																		  Integer.toString(getResources().getInteger(R.integer.defaultUpdateInterval))));
		if (updateInterval == YambaApplication.UPDATE_INTERVAL_NEVER) {
			Log.d(TAG, "Update interval is never");
			
			//unregister alarm
			cancelUpdateAlarm();
			
			return;
		}
				
		// Setup alarm service to wake up and start service periodically
		//FIXME:  add permissions?
		//get alarm manager
		AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
		alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME, 0, updateInterval, pendingIntent);
	}
	
	public boolean cancelUpdateAlarm() {
		Context context = getApplicationContext();
		// Create the pending intent
		PendingIntent pendingIntent = createUpdateAlarmPendingIntent();

		//get alarm manager
		AlarmManager alarmManager = (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
		// Cancel alarms
		try {
		    alarmManager.cancel(pendingIntent);
		    return true;
		} catch (Exception e) {
		    Log.e(TAG, "AlarmManager update was not canceled. " + e.toString());
		    return false;
		}
	}
	
	public PendingIntent createUpdateAlarmPendingIntent() {
		Context context = getApplicationContext();
		Intent intent = new Intent(context, UpdaterService.class);
		return PendingIntent.getService(context, -1, intent, PendingIntent.FLAG_UPDATE_CURRENT);
	}
}
